using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Threshold is an algorithm to apply thresholding to an image.
    /// </summary>
    public class Threshold4 : Threshold
    {
        protected bool m_bUseTwoThreshold = true;
        protected byte m_nMin = 20;
        protected byte m_nMax = 200;
        protected bool m_bUseGrayThreshold = false;
        //pixel arrays for input and output images

        /// <summary>
        /// default constructor
        /// </summary>
        public Threshold4()
        {
        }

        /// <summary>
        /// Determine threshold you might also use two thresholds
        /// </summary>
        /// <param name="_nValue"></param>
        public Threshold4(byte _nValue)
            : this(false, _nValue, 0)
        {

        }

        /// <summary>
        /// Constructs a new Threshold
        /// </summary>
        public Threshold4(bool _bUseTwoThreshold, byte _nMin, byte _nMax)
        {
            m_bUseTwoThreshold = _bUseTwoThreshold;
            m_nMin = _nMin;
            m_nMax = _nMax;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_threshold4"></param>
        internal Threshold4(Threshold4 _threshold4)
        {
            this.m_bUseTwoThreshold = _threshold4.m_bUseTwoThreshold;
            this.m_nMin = _threshold4.m_nMin;
            this.m_nMax = _threshold4.m_nMax;
        }

        /// <summary>
        /// gets or sets if two thresholds should be used
        /// </summary>
        public bool UseTwoThreshold
        {
            get { return m_bUseTwoThreshold; }
            set { m_bUseTwoThreshold = value; }
        }

        /// <summary>
        /// Gets or sets min threshold
        /// </summary>
        public byte Min
        {
            get { return m_nMin; }
            set { m_nMin = value; }
        }

        /// <summary>
        /// Gets or sets max threshold
        /// </summary>
        public byte Max
        {
            get { return m_nMax; }
            set { m_nMax = value; }
        }

        /// <summary>
        /// Executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            if (m_bUseTwoThreshold)
            {
                TwoThreshold(_bitmap, m_nMin, m_nMax, m_bUseGrayThreshold);
            }
            else
            {
                Threshold(_bitmap, m_nMin);
            }
            return _bitmap;
        }

        /// <summary>
        /// Applies the thresholding operator with the specified threshold
        /// value to the specified image array
        /// </summary>
        /// <param name="_bitmap">src pixel array representing image to be thresholded</param>
        /// <param name="value">value threshold value</param>
        public void Threshold(UnsafeBitmap _bitmap, int value)
        {
            ApplyThreshold(_bitmap, value, m_bUseGrayThreshold);
        }

        /// <summary>
        /// src pixel array representing image to be thresholded.
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="low">The low.</param>
        /// <param name="high">The high.</param>
        /// <param name="_bUseGrayThreshold">if set to <c>true</c> [_b use gray threshold].</param>
        public void TwoThreshold(UnsafeBitmap _bitmap, byte low, byte high, bool _bUseGrayThreshold)
        {
            ApplyTwoThreshold(_bitmap, low, high, _bUseGrayThreshold);
        }

        /// <summary>
        /// applies threshold to black-white images
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="thresh">The thresh.</param>
        /// <param name="_bUseGrayThreshold">if set to <c>true</c> [_b use gray threshold].</param>
        protected void ApplyThreshold(UnsafeBitmap _bitmap, int thresh, bool _bUseGrayThreshold)
        {
            int blue = 0;

            UnsafeBitmap bitmap = _bitmap;
            Color color = Color.Transparent;
            int nColorResult = 0;
            int nValue = 0;

            int nForegroundColorInt = ColorFunctions.ToIntRGB(m_ColorForeground);
            int nBackgroundColorInt = ColorFunctions.ToIntRGB(m_ColorBackground);
            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            int x = 0;
            int y = 0;

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    color = bitmap.GetPixel(x, y);
                    if (_bUseGrayThreshold)
                    {
                        nValue = ColorFunctions.Gray(color);
                    }
                    else
                    {
                        nValue = color.ToArgb();
                        blue = nValue & 0x000000ff;
                    }
                    nValue = (int)((blue >= thresh) ? nForegroundColorInt : nBackgroundColorInt);
                    bitmap.SetPixel(x, y, Color.FromArgb(nValue));
                }
            }
            bitmap.Dispose();
        }

        /// <summary>
        /// applies two thresholds to black and white image
        /// if a pixel is between the threshold values then colour it white
        /// otherwise it should be black
        /// </summary>
        /// <param name="_bitmap">The _bitmap.</param>
        /// <param name="_nLow">The _n low.</param>
        /// <param name="_nHigh">The _n high.</param>
        /// <param name="_bUseGrayThreshold">if set to <c>true</c> [_b use gray threshold].</param>
        private void ApplyTwoThreshold(UnsafeBitmap _bitmap, byte _nLow, byte _nHigh, bool _bUseGrayThreshold)
        {
            int blue = 0;

            UnsafeBitmap bitmap = _bitmap;
            int nColorResult = 0;
            int nValue = 0;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            Color color = Color.Transparent;
            Color colorBlack = Definitions.Black;
            Color colorWhite = Definitions.White;
            Color colorToSet = Color.Transparent;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    color = bitmap.GetPixel(x, y);
                    nValue = color.ToArgb();
                    blue = nValue & 0x000000ff;
                    if ((blue <= _nHigh) && (blue >= _nLow))
                    {
                        colorToSet = colorWhite;
                    }
                    else
                    {
                        colorToSet = colorBlack;
                    }
                    bitmap.SetPixel(x, y, colorToSet);
                }
            }
            bitmap.Dispose();
        }

        /// <summary>
        /// Use this to clone the filter
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Threshold4(this);
        }
    }
}